﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

#endregion

namespace ScrumTable.Common.Text
{
    /// <summary>
    /// Represents a parser for ini (settings) file syntax.
    /// </summary>
    public class IniParser
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private static readonly Regex IniParserExpression = new Regex(@"^(?<key>[^=]*?)=(?<value>.*)$", RegexOptions.Multiline | RegexOptions.Compiled);

        private const string KeyValueSeparator = " = ";

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new parser instance.
        /// </summary>
        public IniParser()
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Converts the given data dictionary entries into a ini string.
        /// </summary>
        /// <param name="toSerialize">Specifies the data dicitionary to serialize.</param>
        /// <returns>Returns the serialized data string.</returns>
        public string ToString(IDictionary<string, string> toSerialize)
        {
            PreCondition.AssertNotNull(toSerialize, "toSerialize");

            return ToString(toSerialize, s => s);
        }

        /// <summary>
        /// Converts the given data dictionary entries into a ini string.
        /// </summary>
        /// <param name="toSerialize">Specifies the data dicitionary to serialize.</param>
        /// <param name="valueSerializer">Specifies the value serializer for multiple values in the Collection.</param>
        /// <returns>Returns the serialized data string.</returns>
        public string ToString(IDictionary<string, ICollection<string>> toSerialize, Func<ICollection<string>, string> valueSerializer)
        {
            PreCondition.AssertNotNull(toSerialize, "toSerialize");
            PreCondition.AssertNotNull(valueSerializer, "valueSerializer");

            return ToString<ICollection<string>>(toSerialize, valueSerializer);
        }

        /// <summary>
        /// Parses the given string and interprets it as an ini syntax.
        /// </summary>
        /// <param name="toParse">Specifies the data to parse (deserialize).</param>
        /// <returns>Returns the deserialized data dictionary.</returns>
        public IDictionary<string, string> Parse(string toParse)
        {
            Dictionary<string, string> parsedData = new Dictionary<string, string>();
            Parse(toParse, (key, value) => { parsedData[key] = value; });
            return parsedData;
        }

        /// <summary>
        /// Parses the given string and interprets it as an ini syntax.
        /// </summary>
        /// <param name="toParse">Specifies the data to parse (deserialize).</param>
        /// <param name="valueParseAction">Specifies the action which is executed on data parsing.</param>
        /// <returns>Returns the deserialized data dictionary.</returns>
        public IDictionary<string, ICollection<T>> Parse<T>(string toParse, Func<string, ICollection<T>> valueParseAction)
        {
            PreCondition.AssertNotNull(valueParseAction, "valueParseAction");

            Dictionary<string, ICollection<T>> parsedData = new Dictionary<string, ICollection<T>>();
            Parse(toParse, (key, value) => { parsedData[key] = valueParseAction(value); });
            return parsedData;
        }

        /// <summary>
        /// Parses the given stream and interprets it as an ini syntax.
        /// </summary>
        /// <param name="toParse">Specifies the data to parse (deserialize).</param>
        /// <returns>Returns the deserialized data dictionary.</returns>
        public IDictionary<string, string> Parse(Stream toParse)
        {
            PreCondition.AssertNotNull(toParse, "toParse");

            using (StreamReader reader = new StreamReader(toParse))
            {
                return Parse(reader.ReadToEnd());
            }
        }

        private void Parse(string toParse, Action<string, string> valueParsedAction)
        {
            foreach (Match dataMatch in IniParserExpression.Matches(toParse ?? string.Empty))
            {
                string key = dataMatch.Groups["key"].Value.Trim();
                string value = dataMatch.Groups["value"].Value.Trim();

                if (!string.IsNullOrEmpty(key) || !string.IsNullOrEmpty(value))
                {
                    valueParsedAction(key, value);
                }
            }
        }

        private string ToString<T>(IDictionary<string, T> toSerialize, Func<T, string> valueSerializer)
        {
            StringBuilder stringOutput = new StringBuilder();

            foreach (KeyValuePair<string, T> dataPair in toSerialize)
            {
                stringOutput.Append(dataPair.Key);
                stringOutput.Append(KeyValueSeparator);
                stringOutput.AppendLine(valueSerializer(dataPair.Value));
            }
            return stringOutput.ToString();
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}